package tech

import (
	"ssp/set"
)

func (s *Solve) CounterDeathExtRect(g *Grid) (used bool, results []*Result) {
	return g.counterDeathExtRect()
}

func (s *Solve) CounterDeathExtRectT(g *Grid) (used bool, results []*Result) {
	return g.counterDeathExtRectT()
}

func (s *Solve) CounterDeathExtRectC(g *Grid) (used bool, results []*Result) {
	return g.counterDeathExtRectC()
}

func (g *Grid) counterDeathExtRectC() (used bool, results []*Result) {

	for chute := range 3 {
		lines := g.rows[chute*3 : chute*3+3]
		if ok, rs := g.doCDERLinesC(lines); ok {
			used = true
			results = append(results, rs...)
			if !EXHAUSTIVE {
				return
			}
		}
	}
	for chute := range 3 {
		lines := g.cols[chute*3 : chute*3+3]
		if ok, rs := g.doCDERLinesC(lines); ok {
			used = true
			results = append(results, rs...)
			if !EXHAUSTIVE {
				return
			}
		}
	}
	return
}

func (g *Grid) doCDERLinesC(lines []House) (used bool, results []*Result) {
	candsCells := map[set.Set][]*Cell{}
	for _, line := range lines {
		for _, cell := range line.cells {
			if !cell.solved() && cell.cands.Count() == 2 {
				candsCells[cell.cands] = append(candsCells[cell.cands], cell)
			}
		}
	}
	for cands, cells := range candsCells {
		if len(cells) != 5 {
			continue
		}
		rowCount := [9]int{}
		colCount := [9]int{}
		for _, cell := range cells {
			rowCount[cell.rowNum]++
			colCount[cell.colNum]++
		}
		var rowNum, colNum int
		var rows, cols int
		for i, count := range rowCount {
			if count != 0 {
				rows++
			}
			if count == 1 {
				rowNum = i
			}
		}
		for i, count := range colCount {
			if count != 0 {
				cols++
			}
			if count == 1 {
				colNum = i
			}
		}
		if rows != 3 || cols != 3 {
			continue
		}

		rmCell := g.rows[rowNum].cells[colNum]
		if rmCell.solved() {
			continue
		}
		if rmCell.cands.Intersect(cands).Count() == 0 {
			continue
		}
		used = true
		if OUTPUTRESULT {
			result := new(Result)
			result.addRemoved(cands, []*Cell{rmCell})
			g1 := result.getHelpCandsGroup()
			g1.add(cands, cells)
			result.setStrategyName("Counter Extended Death Rectangle")
			results = append(results, result)
		}
		if CHANGE {
			removeCands(cands, []*Cell{rmCell})
		}
		if !EXHAUSTIVE {
			return
		}
	}

	return
}

func (g *Grid) counterDeathExtRectT() (used bool, results []*Result) {

	for chute := range 3 {
		lines := g.rows[chute*3 : chute*3+3]
		if ok, rs := g.doCDERLinesT(lines); ok {
			used = true
			results = append(results, rs...)
			if !EXHAUSTIVE {
				return
			}
		}
	}

	for chute := range 3 {
		lines := g.cols[chute*3 : chute*3+3]
		if ok, rs := g.doCDERLinesT(lines); ok {
			used = true
			results = append(results, rs...)
			if !EXHAUSTIVE {
				return
			}
		}
	}

	return
}

func (g *Grid) counterDeathExtRect() (used bool, results []*Result) {

	for chute := range 3 {
		for _, lines := range combine(g.rows[chute*3:chute*3+3], 2) {
			if ok, rs := doCDERLines(lines); ok {
				used = true
				results = append(results, rs...)
				if !EXHAUSTIVE {
					return
				}
			}
		}
	}

	for chute := range 3 {
		for _, lines := range combine(g.cols[chute*3:chute*3+3], 2) {
			if ok, rs := doCDERLines(lines); ok {
				used = true
				results = append(results, rs...)
				if !EXHAUSTIVE {
					return
				}
			}
		}
	}
	return
}

func (g *Grid) doCDERLinesT(lines []House) (used bool, results []*Result) {
	numss := make([][]int, 3)
	for i := range 9 {
		if !lines[0].cells[i].solved() && !lines[1].cells[i].solved() && !lines[2].cells[i].solved() {
			numss[i/3] = append(numss[i/3], i)
		}
	}
	for _, cellNumss := range combine(numss, 2) {
		for _, cellNums := range mzip(cellNumss) {
			if ok, rs := g.doCDERectT(lines, cellNums); ok {
				used = true
				results = append(results, rs...)
				if !EXHAUSTIVE {
					return
				}
			}
		}
	}

	return
}

func doCDERLines(lines []House) (used bool, results []*Result) {
	numss := make([][]int, 3)
	for i := range 9 {
		if !lines[0].cells[i].solved() && !lines[1].cells[i].solved() {
			numss[i/3] = append(numss[i/3], i)
		}
	}
	for _, cellNums := range mzip(numss) {
		if ok, rs := doCDERect(lines, cellNums); ok {
			used = true
			results = append(results, rs...)
			if !EXHAUSTIVE {
				return
			}
		}
	}
	return
}

func (g *Grid) doCDERectT(lines []House, cellNums []int) (used bool, results []*Result) {
	var newLines []House
	var newCellNums []int
	if lines[0].kind == 0 {
		newLines = []House{g.cols[cellNums[0]], g.cols[cellNums[1]]}
		newCellNums = []int{lines[0].cells[0].rowNum, lines[1].cells[0].rowNum, lines[2].cells[0].rowNum}
	} else {
		newLines = []House{g.rows[cellNums[0]], g.rows[cellNums[1]]}
		newCellNums = []int{lines[0].cells[0].colNum, lines[1].cells[0].colNum, lines[2].cells[0].colNum}
	}

	if ok, rs := doCDERect1(newLines, newCellNums); ok {
		used = true
		results = append(results, rs...)
	}
	return
}

func doCDERect(lines []House, cellNums []int) (used bool, results []*Result) {
	if ok, rs := doCDERect1(lines, cellNums); ok {
		used = true
		results = append(results, rs...)
	}
	return
}

func doCDERect1(lines []House, cellNums []int) (used bool, results []*Result) {
	//fmt.Println(lines, cellNums)
	candss := make([]set.Set, 2)
	for i, line := range lines {
		for _, cellNum := range cellNums {
			candss[i] = candss[i].Union(line.cells[cellNum].cands)
		}
	}
	//fmt.Println(lines[0].cells[0].colNum, lines[1].cells[0].colNum, cellNums)
	//fmt.Println(candss)
	var baseCands set.Set
	var rmLine House
	if candss[0].Count() == 3 {
		rmLine = lines[1]
		baseCands = candss[0]
	} else if candss[1].Count() == 3 {
		rmLine = lines[0]
		baseCands = candss[1]
	} else {
		return
	}
	//fmt.Println(rmLine, baseCands)
	count := 0
	var rmCell *Cell
	for _, cellNum := range cellNums {
		cell := rmLine.cells[cellNum]
		cands := cell.cands.Diff(baseCands)
		if cands.Count() > 0 {
			count++
			rmCell = cell
		}
	}
	//fmt.Println(rmCell, count)
	if count != 1 {
		return
	}
	used = true
	if OUTPUTRESULT {
		result := new(Result)
		result.addRemoved(baseCands, []*Cell{rmCell})
		g1 := result.getHelpCandsGroup()
		for _, line := range lines {
			for _, cellNum := range cellNums {
				g1.add(baseCands, line.cells[cellNum:cellNum+1])
			}
		}
		result.setStrategyName("Counter Extended Death Rectangle")
		results = append(results, result)
	}
	if CHANGE {
		removeCands(baseCands, []*Cell{rmCell})
	}
	return

}
